﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//
namespace roslynCodeGenFor2_0
{
    public class CodeGen
    {
        [Generator]
        public class AutoNotifySourceGenerator : ISourceGenerator
        {
            public void Initialize(GeneratorInitializationContext context)
            {
                // 注册语法接收器
                context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
            }

            public void Execute(GeneratorExecutionContext context)
            {
                if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
                    return;

                // 生成 AutoNotifyAttribute 特性
                string attributeSource = @"
using System;

namespace roslynCodeGenFor2_0
{
    [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
    sealed class AutoNotifyAttribute : Attribute
    {
        public AutoNotifyAttribute()
        {
        }
    }
}";
                context.AddSource("AutoNotifyAttribute.cs", attributeSource);

                // 处理每个包含标记字段的类
                foreach (var classDecl in receiver.CandidateClasses)
                {
                    ProcessClass(classDecl, context);
                }
            }

            private void ProcessClass(ClassDeclarationSyntax classDecl, GeneratorExecutionContext context)
            {
                string namespaceName = GetNamespace(classDecl);
                string className = classDecl.Identifier.Text;

                // 获取所有带 AutoNotify 特性的字段
                var fields = classDecl.Members
                    .OfType<FieldDeclarationSyntax>()
                    .Where(f => f.AttributeLists
                        .SelectMany(al => al.Attributes)
                        .Any(a => a.Name.ToString() == "AutoNotify"));

                if (!fields.Any())
                    return;

                var sourceBuilder = new StringBuilder($@"
using System.ComponentModel;

namespace {namespaceName}
{{
    public partial class {className} : INotifyPropertyChanged
    {{
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {{
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }}
");

                // 为每个字段生成属性
                foreach (var field in fields)
                {
                    foreach (var variable in field.Declaration.Variables)
                    {
                        string fieldName = variable.Identifier.Text;
                        string propertyName = char.ToUpper(fieldName[0]) + fieldName.Substring(1);
                        string typeName = field.Declaration.Type.ToString();

                        sourceBuilder.Append($@"
        public {typeName} {propertyName}
        {{
            get => {fieldName};
            set
            {{
                if (!Equals({fieldName}, value))
                {{
                    {fieldName} = value;
                    OnPropertyChanged(nameof({propertyName}));
                }}
            }}
        }}
");
                    }
                }

                sourceBuilder.Append("    }\n}");
                context.AddSource($"{className}.Generated.cs", sourceBuilder.ToString());
            }

            private string GetNamespace(ClassDeclarationSyntax classDecl)
            {
                var candidate = classDecl.Parent as NamespaceDeclarationSyntax;
                return candidate?.Name.ToString() ?? "roslynCodeGenFor2_0";
            }
        }

        class SyntaxReceiver : ISyntaxReceiver
        {
            public List<ClassDeclarationSyntax> CandidateClasses { get; } = new List<ClassDeclarationSyntax>();

            public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
            {
                if (syntaxNode is ClassDeclarationSyntax classDeclaration)
                {
                    // 查找包含 AutoNotify 特性的字段
                    if (classDeclaration.Members
                        .OfType<FieldDeclarationSyntax>()
                        .Any(f => f.AttributeLists
                            .SelectMany(al => al.Attributes)
                            .Any(a => a.Name.ToString() == "AutoNotify")))
                    {
                        CandidateClasses.Add(classDeclaration);
                    }
                }
            }
        }
    }
}